﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BawangMemo.BackendModels
{
    public class Algo : IReciter
    {
        #region Definition
        public List<WordInfo> _words;
        private List<Bucket> _buckets;
        public int CurrentPos;
        public int newWordNum;     //每个bucket中新词的数目
        public int maxCountEachWord = 3;   //最多遍数
        private int maxBucket; // 当前最大的bucket
        private int currentBucketId; //最小bucketId作为当前需要处理的bucket
        private Bucket currentBucket;
        private int theshold; //大于theshold时word从WordList中删除，不再丢入后面的bucket
        private Random ran = new Random();
        #endregion

        private class Bucket
        {
            /*
             * 用来存储每一天需要背诵的单词
             * 分别保存已经背过的旧单词和没有背过的新单词
             * 三个方法判断Bucket里剩余的单词情况
             */

            public List<WordInfo> _oldWordList = new List<WordInfo>();
            public List<WordInfo> _newWordList = new List<WordInfo>();

            public bool isEmpty()
            {
                if (isOldWordListEmpty() && isNewWordListEmpty())
                    return true;
                else
                    return false;
            }

            public bool isOldWordListEmpty()
            {
                if (_oldWordList.Count == 0)
                    return true;
                else
                    return false;
            }

            public bool isNewWordListEmpty()
            {
                if (_newWordList.Count == 0)
                    return true;
                else
                    return false;
            }
        }

        private int currentWordIndex = 0;

        public int CurrentWordIndex
        {
            get
            {
                return currentWordIndex;
            }
        }

        public int CurrentBucketId
        {
            get
            {
                return currentBucketId;
            }
        }

        public int TotalBucket
        {
            get
            {
                return maxBucket;
            }
        }

        public int TodayTotal
        {
            get;
            private set;
        }

        public int TodayRecited
        {
            get;
            private set;
        }

        public bool IsAllFinish
        {
            get
            {
                return isAllFinish;
            }
        }

        private bool isAllFinish = false;

        public Algo(List<WordInfo> _words, int CurrentPos, int newWordCount)
        {
            this.currentWordIndex = CurrentPos;
            this._words = _words;
            this.CurrentPos = CurrentPos;
            _buckets = new List<Bucket>();

            newWordNum = newWordCount;

            // initialize useful variables
            
            currentBucketId = 1000000;
            theshold = 30;

            maxBucket = 0;
            int count = 0;

            if (_words[0].Bucket == 0 || _words[_words.Count - 1].Bucket == 0) //第一次背单词，需要把每个生词放入对应的bucket中
            {
                _buckets.Add(new Bucket());
                for (int i = 0; i < _words.Count; i++)
                {
                    if (i % newWordNum == 0)
                    {
                        _buckets.Add(new Bucket());
                        count++;
                    }

                    _buckets[count]._newWordList.Add(_words[i]);
                    _words[i].Bucket = count;
                }
                currentBucketId = 1;
            }

            else // 把每个单词丢入相应的bucket中，其中新词丢入_newWordList，旧词丢入_oldWordList
            {
                _buckets.Add(new Bucket());
                for (int i = 0; i < _words.Count; i++)
                {
                    if (!_words[i].Activated)     //如果这个单词已经不会再被背诵，则不处理它
                        continue;
                    if (_words[i].Bucket < currentBucketId)
                        currentBucketId = _words[i].Bucket;

                    if (_words[i].Bucket > maxBucket)//如果Bucket不够多，添加新的Bucket
                    {
                        for (int j = maxBucket + 1; j <= _words[i].Bucket; j++)
                            _buckets.Add(new Bucket());
                        maxBucket = _words[i].Bucket;
                    }

                    if (_words[i].isNewWord())//判断是否是旧单词，从而决定将其放入旧单词表还是新单词表
                        _buckets[_words[i].Bucket]._newWordList.Add(_words[i]);
                    else
                        _buckets[_words[i].Bucket]._oldWordList.Add(_words[i]);
                }
            }

            if (currentBucketId >= 1000000)
            {
                currentBucketId = 0;
                isAllFinish = true;
                currentBucket = _buckets[currentBucketId];
                TodayTotal = TodayRecited = 1;
            }
            else
            {
                currentBucket = _buckets[currentBucketId];
                TodayTotal = (currentBucket._newWordList.Count + currentBucket._oldWordList.Count) * maxCountEachWord;
                TodayRecited = currentBucket._oldWordList.Sum(word => word.MemCount) +
                    currentBucket._newWordList.Sum(word => word.MemCount);
            }
        }

        public WordInfo NextWord()
        {
            if (isAllFinish)
                return null;
            WordInfo wordInfo = NextWordPrivate();
            if (wordInfo != null)
                currentWordIndex = Math.Max(currentWordIndex, wordInfo.WordId - 1);
            return wordInfo;
        }

        private WordInfo NextWordPrivate()
        {
            /*
             * 提供下一个需要背诵的单词
             * 如果今天计划已经背完，返回null
             */

            WordInfo currentWord;

            if (currentBucket.isEmpty())  //当前bucket为空的情况
            {
                currentBucketId++;
                return null;    //当前bucket没有词了
            }

            if (currentBucket.isOldWordListEmpty())  //当前bucket _oldWordList为空的情况
            {
                currentWord = currentBucket._newWordList[0];
                currentBucket._oldWordList.Add(currentWord);
                currentBucket._newWordList.Remove(currentWord);
                return currentWord;
            }
            else if (currentBucket.isNewWordListEmpty()) // 当前bucket _newWordList为空的情况
            {
                currentWord = currentBucket._oldWordList[ran.Next(currentBucket._oldWordList.Count)];
                return currentWord;
            }
            else //均不为空的情况下，按公式计算概率，选取旧单词或新单词
            {
                if (currentBucket._oldWordList.Count <= currentBucket._newWordList.Count)
                {
                    if (ran.NextDouble() > Math.Pow(1.0 * currentBucket._oldWordList.Count / currentBucket._newWordList.Count, 0.25) / 2)
                    {
                        currentWord = currentBucket._newWordList[0];
                        currentBucket._oldWordList.Add(currentWord);
                        currentBucket._newWordList.Remove(currentWord);
                        return currentWord;
                    }
                    else
                    {
                        currentWord = currentBucket._oldWordList[ran.Next(currentBucket._oldWordList.Count)];
                        return currentWord;
                    }
                }
                else
                {
                    if (ran.NextDouble() <= Math.Pow(1.0 * currentBucket._newWordList.Count / currentBucket._oldWordList.Count, 0.25) / 2)
                    {
                        currentWord = currentBucket._newWordList[0];
                        currentBucket._oldWordList.Add(currentWord);
                        currentBucket._newWordList.Remove(currentWord);
                        return currentWord;
                    }
                    else
                    {
                        currentWord = currentBucket._oldWordList[ran.Next(currentBucket._oldWordList.Count)];
                        return currentWord;
                    }
                }
            }

        }

        public void UserResponse(WordInfo _word, int proficiency)
        {
            /*
             * 接收来自用户的反馈，即用户对于该单词的熟练度反应
             * 要求上层程序在调用一次NextWord之后调用该程序给予反馈
             */
            if (isAllFinish)
                return;

            ++TodayRecited;

            int oldBucket;
            int newBucket;
            _word.Proficiency = (_word.MemCount * _word.Proficiency + 10 * proficiency) / (_word.MemCount + 1);
            _word.TotalProficiency = (_word.TotalMemCount * _word.TotalProficiency + (10.0 * proficiency) / 3) / (_word.TotalMemCount + 1);
            _word.MemCount++;
            _word.TotalMemCount++;
            if (_word.MemCount >= maxCountEachWord)  //如果单词计数大于最多遍数时
            {
                currentBucket._oldWordList.Remove(_word);
                oldBucket = _word.Bucket;

                if (_word.isNewWord())  //若是新词的情况
                    newBucket = _word.Bucket + Convert.ToInt32(Math.Ceiling(Math.Pow(1.3, Convert.ToDouble(_word.Proficiency) / 6.0 - 3.0)));
                else  //不是新词的情况
                    newBucket = _word.Bucket + (_word.Bucket - _word.OldBucket) * Convert.ToInt32(Math.Ceiling(Math.Pow(1.3, Convert.ToDouble(_word.Proficiency) / 6.0 - 3.0)));

                if (newBucket - oldBucket > theshold) //若下一个目标bucket与当前bucket之差大于theshold的情况
                {
                    _word.Proficiency = 0;
                    _word.MemCount = 0;
                    _word.Activated = false;
                }
                else
                {
                    _word.Bucket = newBucket;
                    _word.OldBucket = oldBucket;
                    _word.Proficiency = 0;
                    _word.MemCount = 0;
                }
            }
        }
    }
}
